package com.db.service.realm;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.db.dao.SysMenuDao;
import com.db.dao.SysRoleMenuDao;
import com.db.dao.SysUserDao;
import com.db.dao.SysUserRoleDao;
import com.db.pojo.SysMenu;
import com.db.pojo.SysRoleMenu;
import com.db.pojo.SysUser;
import com.db.pojo.SysUserRole;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thymeleaf.util.StringUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
public class ShiroUserRealm extends AuthorizingRealm {

	@Autowired
	private SysUserDao sysUserDao;

	@Autowired
	private SysUserRoleDao sysUserRoleDao;

	@Autowired
	private SysRoleMenuDao sysRoleMenuDao;

	@Autowired
	private SysMenuDao sysMenuDao;

	/**
	 * 设置凭证匹配器
	 * 加密算法与添加用户一样
	 *
	 * @param principals
	 * @return
	 */

	@Override
	public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
		HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
		matcher.setHashAlgorithmName("MD5");
		matcher.setHashIterations(1);
		super.setCredentialsMatcher(matcher);
	}


	/**
	 * 通过此方法完成认证数据的获取及封装
	 * 系统底层将认证数据传输给认证器
	 * 由认证管理器完成认证操作
	 *
	 * @param token
	 * @return
	 * @throws AuthenticationException
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		String username = upToken.getUsername(); //页面用户输入的用户名
		/* 从数据库查询该用户名是否存在 */
		QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
		wrapper.eq("username", username);
		SysUser user = sysUserDao.selectOne(wrapper);
		if (user == null)
			throw new UnknownAccountException(); //用户不存在
		if (user.getValid() == 0)
			throw new LockedAccountException(); //用户已被禁用

		//封装用户信息
		ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt()); //盐值
		SimpleAuthenticationInfo simpleAuthenticationInfo =
				new SimpleAuthenticationInfo(user, user.getPassword(), credentialsSalt, getName());

		return simpleAuthenticationInfo;
	}

	/**
	 * 通过此方法完成授权信息的获取及封装
	 *
	 * @param principals
	 * @return
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		//获取用户登录信息
		SysUser sysUser = (SysUser) principals.getPrimaryPrincipal();
		Integer userId = sysUser.getId();

		/* 根据用户id获取对应的角色信息 */
		QueryWrapper<SysUserRole> wrapper = new QueryWrapper<>();
		wrapper.select("role_id").eq("user_id", userId);
		List<SysUserRole> roleIds = sysUserRoleDao.selectList(wrapper);
		if (roleIds == null | roleIds.size() == 0)
			throw new AuthorizationException();

		/* 根据角色id,获取菜单id */
		QueryWrapper<SysRoleMenu> queryWrapper = new QueryWrapper<>();
		queryWrapper.select("menu_id").in("role_id", roleIds);
		List<SysRoleMenu> menuIds = sysRoleMenuDao.selectList(queryWrapper);
		if (menuIds == null || menuIds.size() == 0)
			throw new AuthorizationException();

		/* 根据菜单id获取菜单授权信息 */
		QueryWrapper<SysMenu> menuQueryWrapper = new QueryWrapper<>();
		menuQueryWrapper.select("permission").in("id", menuIds);
		List<SysMenu> permission = sysMenuDao.selectList(menuQueryWrapper);

		/* 对获取出的授权信息进行封装 */
		Set<String> set = new HashSet<>();
		for (SysMenu menu :
				permission) {
			if (StringUtils.isEmptyOrWhitespace(menu.getPermission())) {
				set.add(menu.getPermission());
			}
		}

		/* 封装后返回给授权管理器 */
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		info.setStringPermissions(set);
		return info;
	}
}
